home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
051-075
/
disk_054
/
hanoi
/
hanoi.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-06
|
15KB
|
445 lines
/* hanoi.c
**
** Towers of Hanoi for the Amiga, by Ali Ozer (ali@Score.Stanford.Edu).
** Originally written June '86, revised February '87. This program may be
** freely distributed. Please do not delete the author's name & picture: 8-)
**
** This program will create a window within the workbench screen and start
** solving the Towers of Hanoi problem (with 5 disks). The main purpose
** of this program is to sit in the side and absorb unused cycles --- By
** default it runs at priority -20...
**
** This program can be started from the CLI or the Workbench. If started
** from the CLI, it will accept command line arguments to change the various
** parameters. (Give "?" as an argument to see what arguments are expected.)
** If started from the Workbench, the program will open a window and explicitly
** ask for the parameters. Hit <return> or ctrl-\ to use the default values.
**
** The idea for this program came from a similar program that runs on some
** Xerox Lisp machine. (I think it was some Xerox machine, I just saw it
** run one day as I was watching a friend work on one of them...)
**
** Future improvements to this code might include (if anyone wishes to
** play with this program) making the disks look better and making them
** move pixel by pixel instead of jumping as they do now.
**
** Thanks to Lee Taran (who was a few weeks ahead of me in solving the great
** mysteries of the Rom Kernel Manual, back in May '86, when we first got the
** Amiga --- Most of the initialization code is from her), and Tom Rokicki,
** who convinced us to get an Amiga in the first place and who also posted the
** code for the OpenInfoWindow routine used near the end of this program...
*/
#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include <stdio.h>
/* Default values. Actually there are some more values in the program
** that should be #define'd here, such as the default speed, default number
** of disks, etc, but then I would have to have my strings work more
** dynamically, and, and, and... (excuses excuses)
*/
#define MAXDISKS 25
#define WIDTH 180
#define HEIGHT 60
#define WINDOWX (625-WIDTH)
#define WINDOWY 14
#define TEXTDELAY 6L /* Time to wait after text */
#define MINWIDTHFORTEXT (4 + 8 * 7) /* 8: Font Width, 7: #chars */
FILE *fopen(), *input, *output, temp; /* For fputs/fgets console I/O. */
char *fgets();
void fputs();
void *OpenLibrary(), *OpenWindow(), *FindTask(), *IntuitionBase, *GfxBase;
BYTE disks[3][MAXDISKS+1]; /* We have 3 towers with upto MAXDISKS disks */
int topdisk[3]; /* Free location on each tower (0 = all free) */
int towerloc[4]; /* Pixel location of each tower (4th = imag.) */
/* Tons of globals... */
int dx, dy, height, width, top, moveto,
numsteps, numdisks, wbench, writetext;
long movedelay, windowcolor, diskcolor,
outlinecolor, beepcolor, textcolor, priority;
struct NewWindow WindowInfo =
{ WINDOWX, WINDOWY, WIDTH, HEIGHT, /* Left, Top, Width, Height */
0, 0, /* Detail pen, Block pen (-1 = use screens) */
CLOSEWINDOW | NEWSIZE | REFRESHWINDOW | SIZEVERIFY, /* IDCMPflags */
WINDOWDEPTH | WINDOWSIZING |
SMART_REFRESH | WINDOWCLOSE | WINDOWDRAG, /* Flags */
NULL, NULL, NULL, /* FirstGadget, Menu Checkmark, Title */
NULL, NULL, /* Screen, Bitmap */
0, 0, 640, 200, /* Min Width/Height Max Width/Height */
WBENCHSCREEN /* Type */
};
/* Structure to hold all the window info maintained by Intuition */
struct Window *HanoiWindow;
struct RastPort *HanoiRPort;
/* A macro to make dealing with undefined argv's easier... */
#define ARGV(n) (argc <= n ? "" : argv[n])
main(argc, argv)
int argc;
char *argv[];
{
int i, cnt, GetNum(), GetArg();
struct Task *thistask;
if (wbench = (argc == 0)) { /* Ie, started from the workbench */
if (!OpenInfoWindow()) exit (1);
fputs ("Enter parameters, <CR> or ^\\ for defaults\n", output);
numdisks = GetNum ("Number of disks? [1..25, default 5] ", 1, 25, 5);
movedelay = 12-GetNum ("Speed? [1..12, default 4] ", 1, 12, 4);
windowcolor = GetNum ("Background Color? [0..3, default 3] ", 0, 3, 3);
textcolor = GetNum ("Text Color? [0..3, default 3] ", 0, 3, 3);
diskcolor = GetNum ("Disk Color [0..3, default 2] ", 0, 3, 2);
outlinecolor= GetNum ("Outline Color [0..3, default 2] ", 0, 3, 2);
priority = -20L;
CloseInfoWindow ();
} else { /* Started from the CLI */
numdisks = GetArg (ARGV(1), 1, 25, 5);
movedelay = 12-GetArg (ARGV(2), 1, 12, 4);
windowcolor = GetArg (ARGV(3), 0, 3, 3);
textcolor = GetArg (ARGV(4), 0, 3, 3);
diskcolor = GetArg (ARGV(5), 0, 3, 2);
outlinecolor= GetArg (ARGV(6), 0, 3, 2);
WindowInfo.LeftEdge = GetArg (ARGV(7), 0, 640 - WIDTH, WINDOWX);
WindowInfo.TopEdge = GetArg (ARGV(8), 0, 200 - HEIGHT, WINDOWY);
priority = GetArg (ARGV(9), 0, 8, 0) * 5 - 20;
};
if (thistask = FindTask (NULL)) SetTaskPri (thistask, (long)priority);
numsteps = (numdisks * 2) + 4;
writetext = (windowcolor != textcolor);
WindowInfo.DetailPen = windowcolor;
WindowInfo.BlockPen = windowcolor;
WindowInfo.MinWidth = numsteps * 3;
WindowInfo.MinHeight = numdisks * 2 + 4 + writetext * 10;
WindowInfo.Height += (writetext * 10);
/* Make sure beepcolor is different than disk or window color */
if ((beepcolor = ((diskcolor - 1) & 3)) == windowcolor)
beepcolor = (beepcolor - 1) & 3;
OpenLibraries();
SetupEnvironment();
ReCalculateParameters();
InitializeTowers();
ClearWindow();
DrawAllDisks(diskcolor);
for (cnt = 0; cnt < 10; cnt++) {
CheckForMessages (); Delay (20L);
}
while (1)
for (i = 0; i < 3; i++) {
MoveDisks(i, (i+1)%3, (i+2)%3, numdisks);
DrawAllDisks (beepcolor); Delay (6L); DrawAllDisks (diskcolor);
for (cnt = 0; cnt < numdisks * 3; cnt++) {
CheckForMessages (); Delay (20L);
}
}
}
/* Called after resizing to determine new parameters...
*/
ReCalculateParameters()
{
int xstart, i;
top = (writetext ? 9 : 0);
height = HanoiWindow->Height;
width = HanoiWindow->Width;
dx = width / (3 * numsteps);
dy = (height-top) / (numdisks + 2);
xstart = (width - dx * 3 * numsteps) / 2;
for (i = 0; i < 4; i++) towerloc[i] = xstart + i * numsteps * dx;
moveto = top + dy;
}
DrawDiskAtLoc (disk, tower, location, diskcolor, outlinecolor)
int disk, tower, location;
long diskcolor, outlinecolor;
{
if (disk) {
SetAPen (HanoiRPort, diskcolor);
SetOPen (HanoiRPort, outlinecolor);
RectFill (HanoiRPort,
(long)(towerloc[tower] + (numdisks - disk + 1) * dx),
(long)(height - (location + 1) * dy),
(long)(towerloc[tower+1] - 1 - (numdisks - disk + 1) * dx),
(long)(height - (location * dy) - 2));
}
}
DrawAllDisks(color)
long color;
{
int tower, location;
for (tower = 0; tower < 3; tower++)
for (location = 0; location < numdisks; location++)
DrawDiskAtLoc (disks [tower][location], tower,
location, color, outlinecolor);
}
/* InitializeTowers will clear towers 1 and 2 and fill up tower 0.
** To start off, we have disk[0][0] = largest disk, disk[0][1] = next
*/
InitializeTowers()
{
int i;
for (i=0; i<numdisks; i++) {
disks[0][i] = numdisks-i;
disks[1][i] = 0;
disks[2][i] = 0;
}
topdisk[0] = numdisks;
topdisk[1] = 0;
topdisk[2] = 0;
}
/* Write over *everything* in sight, including the borders and the title...
*/
ClearWindow ()
{
SetOPen (HanoiRPort, windowcolor);
SetAPen (HanoiRPort, windowcolor);
RectFill (HanoiRPort, 0L, 0L, (long)(width-1), (long)(height-1));
}
static char titlestring[8] = "X->X ";
/* ShowMoveInfo will print out the current move being made. Note that
** this routine is NOT CALLED at all if the user specifies the text color
** to be the same as the window color... This routine does slow down the
** program, but it makes it a more educational tool...
*/
ShowMoveInfo (fromtower, totower, ndisks)
int fromtower, totower, ndisks;
{
if (ndisks > 9) titlestring[5] = '0' + ndisks / 10;
else titlestring[5] = ' ';
titlestring[6] = '0' + ndisks % 10;
titlestring[0] = 'A' + fromtower;
titlestring[3] = 'A' + totower;
SetAPen (HanoiRPort, textcolor);
SetDrMd (HanoiRPort, JAM2);
Move (HanoiRPort, 1L, 8L);
Text (HanoiRPort, " ", 7L);
SetDrMd (HanoiRPort, JAM1);
Move (HanoiRPort, 1L, 8L);
Text (HanoiRPort, titlestring, 7L);
}
/* This is the recursive Hanoi function! Looks simple, no?
*/
MoveDisks (fromtower, totower, sparetower, ndisks)
int fromtower, totower, sparetower, ndisks;
{
if (writetext && width > MINWIDTHFORTEXT) {
ShowMoveInfo (fromtower, totower, ndisks);
Delay (TEXTDELAY * movedelay);
CheckForMessages ();
}
if (ndisks == 1) /* Base case */
MoveOneDisk (fromtower, totower);
else {
MoveDisks (fromtower, sparetower, totower, ndisks-1);
MoveOneDisk (fromtower, totower);
MoveDisks (sparetower, totower, fromtower, ndisks-1);
}
}
/* This moves (graphically) the top disk from fromtower to totower.
*/
MoveOneDisk (fromtower, totower)
int fromtower, totower;
{
int cnt;
int disk = disks[fromtower][topdisk[fromtower]-1];
if (writetext && width > MINWIDTHFORTEXT)
ShowMoveInfo (fromtower, totower, 1);
for (cnt = topdisk[fromtower]; cnt <= numdisks; cnt++) {
DrawDiskAtLoc (disk, fromtower, cnt-1, windowcolor, windowcolor);
DrawDiskAtLoc (disk, fromtower, cnt, diskcolor, outlinecolor);
Delay (movedelay);
}
topdisk[fromtower]--;
disks[fromtower][topdisk[fromtower]] = 0;
CheckForMessages ();
if (fromtower < totower)
for (cnt = fromtower+1; cnt <= totower; cnt++) {
DrawDiskAtLoc (disk, cnt-1, numdisks, windowcolor, windowcolor);
DrawDiskAtLoc (disk, cnt, numdisks, diskcolor, outlinecolor);
Delay (movedelay);
}
else
for (cnt = fromtower-1; cnt >= totower; cnt--) {
DrawDiskAtLoc (disk, cnt+1, numdisks, windowcolor, windowcolor);
DrawDiskAtLoc (disk, cnt, numdisks, diskcolor, outlinecolor);
Delay (movedelay);
};
CheckForMessages ();
for (cnt = numdisks-1; cnt >= topdisk[totower]; cnt--) {
DrawDiskAtLoc (disk, totower, cnt+1, windowcolor, windowcolor);
DrawDiskAtLoc (disk, totower, cnt, diskcolor, outlinecolor);
Delay (movedelay);
}
disks[totower][topdisk[totower]] = disk;
topdisk[totower]++;
CheckForMessages ();
}
CheckForMessages ()
{
struct IntuiMessage *message, *GetMsg();
ULONG class; /* What kind of message? */
/* Try to get a message. If there is one, process it. */
while (message = GetMsg(HanoiWindow->UserPort)) {
class = message->Class;
ReplyMsg(message);
switch (class) {
case SIZEVERIFY : Wait (1L << HanoiWindow->UserPort->mp_SigBit);
break;
case NEWSIZE : ReCalculateParameters(); /* Fall through */
case REFRESHWINDOW : ClearWindow();
DrawAllDisks(diskcolor);
break;
case CLOSEWINDOW : CloseThings(0); /* CloseThings exits */
default : CloseThings(1); /* CloseThings exits */
}
}
}
OpenLibraries ()
{
if (!(IntuitionBase = OpenLibrary("intuition.library",0L)))
Panic("No intuition library\n");
if (!(GfxBase = OpenLibrary("graphics.library",0L)))
Panic("No graphics library\n");
}
/* Initializes the Hanoi window and sets up the HanoiRPort, used often. */
SetupEnvironment()
{
if (!(HanoiWindow = OpenWindow(&WindowInfo))) Panic ("Can't open window\n");
HanoiRPort = HanoiWindow->RPort;
SetBPen (HanoiRPort, windowcolor);
}
/* Prints out an error message about what went wrong and exits gracefully */
Panic(errormsg)
char *errormsg;
{
if (wbench) DisplayBeep (NULL); /* Simply flash the display... */
else puts (errormsg);
CloseThings(1);
}
/* Closes the Libraries that have been opened and frees up all memory */
CloseThings(error_code)
int error_code;
{
if (HanoiWindow) CloseWindow(HanoiWindow);
if (GfxBase) CloseLibrary(GfxBase);
if (IntuitionBase) CloseLibrary(IntuitionBase);
exit (error_code);
}
int GetNum (prompt, min, max, def)
int min, max, def;
char *prompt;
{
int result, cnt;
char resp[80];
while (1) {
fputs (prompt, output); fflush (output);
fgets (resp, 80, input);
cnt = 0;
while (resp[cnt] == ' ') cnt++;
if (resp[cnt] == '\0' || resp[cnt] == '\n') return (def);
result = 0;
while (resp[cnt] != '\0' && resp[cnt] != '\n' &&
result <= max && result != -1) {
if (resp[cnt] < '0' || resp[cnt] > '9') result = -1;
else result = result * 10 + resp[cnt++] - '0';
};
if (result >= min && result <= max) return (result);
if (result == -1) fputs ("Illegal integer, please try again.\n", output);
else fputs ("Value out of range, please try again.\n", output);
}
}
/* Opens a console window for fputs/fgets compatible I/O. Thanks to Tom
** Rokicki (from whose posting the last 4 lines of this routine come...).
*/
int OpenInfoWindow ()
{
static char consp[] = "CON:30/30/400/80/Hanoi by Ali Ozer";
consp[30] = 214; /* Guess what this does? Run the program and see */
if ((output = fopen (consp, "w+")) == NULL) return (0);
input = &temp;
*input = *output;
return (1);
}
CloseInfoWindow ()
{
fclose (input);
fclose (output);
}
/* Gets a positive integer from argstr. If argstr is empty or "-", return
** defaultnum. Error if argstr is an invalid number or is not in range.
*/
int GetArg (argstr, min, max, def)
int min, max, def;
char *argstr;
{
int result = 0;
if (strlen(argstr) == 0 || !strcmp(argstr,"-")) return (def);
while (*argstr != '\0' && result <= max) {
if (*argstr < '0' || *argstr > '9') IllegalArgs();
result = result * 10 + *argstr++ - '0';
};
if (result >= min && result <= max) return (result);
IllegalArgs ();
}
/* Prints usage line and causes program to terminate. Considering we only
** call GetArg once we know we were called from CLI, we can safely do puts.
*/
IllegalArgs ()
{
puts ("\nUsage:\nHanoi NDisks Speed BGColor TxtColor DskColor OutLineColor X Y Priority");
puts (" NDisks 1..25, Speed 0..12, Colors 0..3 (WorkBench colors)");
puts (" Priority 0..8 (Corresponding to -20..+20)");
puts (" All arguments are optional. Use - to get a default value.\n");
CloseThings (1);
}
/* hanoi.c, by Ali Ozer */